#include <cassert>
#include <functional>

#include "p2pms_client_impl.hpp"
#include "p2pms_log.hpp"
#include "p2pms_util.hpp"
#include "p2p_call_agent.hpp"
#include "sfu_call_agent.hpp"
#include "p2sp_call_agent.hpp"
#include "device_manager.hpp"

#include "api/task_queue/default_task_queue_factory.h"

#include "rtc_base/logging.h"
#include "rtc_base/ssl_adapter.h"
#include "api/create_peerconnection_factory.h"
#include "api/audio_codecs/builtin_audio_decoder_factory.h"
#include "api/audio_codecs/builtin_audio_encoder_factory.h"
#include "api/video_codecs/video_decoder_factory_template.h"
#include "api/video_codecs/video_decoder_factory_template_dav1d_adapter.h"
#include "api/video_codecs/video_decoder_factory_template_libvpx_vp8_adapter.h"
#include "api/video_codecs/video_decoder_factory_template_libvpx_vp9_adapter.h"
#include "api/video_codecs/video_decoder_factory_template_open_h264_adapter.h"
#include "api/video_codecs/video_encoder_factory_template.h"
#include "api/video_codecs/video_encoder_factory_template_libaom_av1_adapter.h"
#include "api/video_codecs/video_encoder_factory_template_libvpx_vp8_adapter.h"
#include "api/video_codecs/video_encoder_factory_template_libvpx_vp9_adapter.h"
#include "api/video_codecs/video_encoder_factory_template_open_h264_adapter.h"

using namespace std::placeholders;

const uint32_t kWaitResTimeoutMs = 5000;
const uint32_t kInitTimeoutMs    = 5000;

namespace p2pms
{

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
P2PMSClientImpl::P2PMSClientImpl() : m_init_promise(std::make_shared<std::promise<bool>>())
{
	
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
P2PMSClientImpl::~P2PMSClientImpl()
{
	if (m_msg_dispatcher) {
		m_msg_dispatcher->Stop();
	}
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
ErrCode P2PMSClientImpl::Init(IMainThreadExecutor* executor, IClientHandler* handler, 
	const std::string& server_addr, rtc::Thread* thread)
{
	if (!executor) {
		LOG_ERR("invalid main thread executor");
		return ERR_INVALID_PARAM;
	}
	m_main_thread_executor = executor;

	if (!handler) {
		LOG_ERR("invalid handler");
		return ERR_INVALID_PARAM;
	}
	m_client_handler = handler;

	if (server_addr.empty()) {
		LOG_ERR("empty server address");
		return ERR_INVALID_PARAM;
	}

	if (!thread) {
		LOG_ERR("invalid sdk thread");
		return ERR_INVALID_PARAM;
	}
	m_sdk_thread = thread;

	rtc::InitializeSSL();

	if (!CreateWebRtcThreads()) {
		LOG_ERR("Create webrtc threads failed");
		return ERR_FAILED;
	}

	if (!DeviceManager::Instance().Init(m_worker_thread.get())) {
		LOG_ERR("Init device manager failed");
		return ERR_FAILED;
	}

	if (!CreatePeerConnectionFactory()) {
		LOG_ERR("Create peer connection factory failed");
		return ERR_FAILED;
	}

	// TODO: 这里是偷懒，会导致程序启动就会占用音频设备，应该在适当的时候开启和停止
	DeviceManager::Instance().StartPlayout();
	Sleep(1000);
	DeviceManager::Instance().StartRecording();

	if (!InitMsgDispatcher(server_addr)) {
		LOG_ERR("Init message dispatcher failed");
		return ERR_FAILED;
	}

	m_renderer_manager.reset(new VideoRendererManager(m_main_thread_executor));

	ScheduleTimer();

	// 等待服务器连接结果
	auto future = m_init_promise->get_future();
	if (future.wait_for(std::chrono::milliseconds(kInitTimeoutMs)) == std::future_status::ready) {
		return future.get() ? ERR_OK : ERR_FAILED;
	} else {
		LOG_ERR("InitImpl timeout waiting for server connected");
		return ERR_TIMEOUT;
	}
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
ErrCode P2PMSClientImpl::Login(const std::string& user_id)
{
	if (user_id.empty()) {
		LOG_ERR("User ID cannot be empty");
		return ERR_INVALID_PARAM;
	}

	m_user_id = user_id;

	m_msg_dispatcher->SendMsg(MakeLoginReqMsg(user_id), LOGIN_RES);

	return ERR_OK;
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void P2PMSClientImpl::Logout()
{
	// 登出逻辑
	// 这里可以添加具体的登出逻辑
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
std::vector<std::string> P2PMSClientImpl::GetUserList()
{
	return m_online_user_list;
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
ErrCode P2PMSClientImpl::MakeCall(CallMode mode, const std::string& callee_id)
{
	if (m_call_agent) {
		LOG_ERR("Call agent already exists, please stop the current call first.");
		return ERR_FAILED;
	}

	if (mode == CallMode::CALL_MODE_INVALID) {
		LOG_ERR("Invalid call mode")
		return ERR_INVALID_PARAM;
	}

	if (callee_id.empty()) {
		LOG_ERR("Callee ID cannot be empty");
		return ERR_INVALID_PARAM;
	}

	if (!m_logined) {
		LOG_ERR("Client is not logged in, cannot start call.");
		return ERR_FAILED;
	}

	if (mode != CallMode::CALL_MODE_SFU && m_user_id == callee_id) {
		LOG_ERR("Callee ID cannot be the same as the caller ID.");
		return ERR_FAILED;
	}

	if (mode == CallMode::CALL_MODE_P2P) {
		m_call_agent = std::make_shared<P2PCallAgent>(
			CallRole::Caller, 
			m_user_id, 
			m_pc_factory, 
			this, 
			m_sdk_thread, 
			m_msg_dispatcher);
	} else if (mode == CallMode::CALL_MODE_P2SP) {
		m_call_agent = std::make_shared<P2SPCallAgent>(
			CallRole::Caller, 
			m_user_id, 
			m_pc_factory,
			this, 
			m_sdk_thread, 
			m_msg_dispatcher);
	} else if (mode == CallMode::CALL_MODE_SFU) {
		m_call_agent = std::make_shared<SFUCallAgent>(
			m_user_id,
			m_pc_factory,
			this,
			m_sdk_thread, 
			m_msg_dispatcher);
	} else {
		LOG_ERR("Invalid call mode: {}", static_cast<int>(mode));
		return ERR_FAILED;
	}

	if (ERR_OK != m_call_agent->Init()) {
		LOG_ERR("Failed to initialize call agent for mode: {}", static_cast<int>(mode));
		m_call_agent.reset();
		return ERR_FAILED;
	}

	if (ERR_OK != m_call_agent->MakeCall(callee_id)) {
		LOG_ERR("Failed to start call agent for mode: {}", static_cast<int>(mode));
		m_call_agent.reset();
		return ERR_FAILED;
	}

	return ERR_OK;
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void P2PMSClientImpl::TerminateCall()
{
	if (!m_call_agent) {
		LOG_ERR("No call agent to stop.");
		return;
	}

	if (!m_call_agent) {
		LOG_ERR("No call agent to stop.");
		return;
	}

	return m_call_agent->TerminateCall();
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
ErrCode P2PMSClientImpl::StartPublish(const MediaDevice& device, MediaInfo& media)
{
	if (!m_call_agent) {
		LOG_ERR("No active call agent");
		return ERR_FAILED;
	}
	
	// 检查设备合法性
	if (device.id.empty() || device.name.empty()) {
		LOG_ERR("Invalid media device: id or name is empty");
		return ERR_INVALID_PARAM;
	}

	std::string media_type;
	if (device.type == MediaDeviceType::AudioInput) {
		media_type = "audio";
	}
	else if (device.type == MediaDeviceType::VideoInput) {
		media_type = "video";
	}
	else {
		LOG_ERR("Unsupported media device type: {}", static_cast<int>(device.type));
		return ERR_INVALID_PARAM;
	}

	// 生成媒体信息
	media.media_type = media_type;
	media.media_name = device.name;
	media.src_name = device.name;
	media.src_id = device.id;
	media.src_type = "device";

	auto iter = m_media_id_map.find(media.src_id);
	if (iter == m_media_id_map.end()) {
		media.media_id = GenerateRandomStr();
		m_media_id_map.insert(std::make_pair(media.src_id, media.media_id));
	}
	else {
		media.media_id = iter->second;
	}

	return m_call_agent->StartPublish(media);
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
ErrCode P2PMSClientImpl::StopPublish(const MediaInfo& media)
{
	return m_call_agent->StopPublish(media);
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
ErrCode P2PMSClientImpl::StartSubscribe(const std::string& user_id, const MediaInfo& media)
{
	if (!m_call_agent) {
		LOG_ERR("No active call agent");
		return ERR_FAILED;
	}

	if (user_id.empty()) {
		LOG_ERR("User ID cannot be empty");
		return ERR_INVALID_PARAM;
	}

	if (ValidateMediaInfo(media) == false) {
		LOG_ERR("Invalid media info: {}", ToString(media));
		return ERR_INVALID_PARAM;
	}

	return m_call_agent->StartSubscribe(user_id, media);
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
ErrCode P2PMSClientImpl::StopSubscribe(const std::string& user_id, const MediaInfo& media)
{
	if (!m_call_agent) {
		LOG_ERR("No active call agent");
		return ERR_FAILED;
	}

	if (user_id.empty()) {
		LOG_ERR("User ID cannot be empty");
		return ERR_INVALID_PARAM;
	}

	if (ValidateMediaInfo(media) == false) {
		LOG_ERR("Invalid media info: {}", ToString(media));
		return ERR_INVALID_PARAM;
	}

	return m_call_agent->StopSubscribe(user_id, media);
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
IRendererManager* P2PMSClientImpl::GetRendererManager()
{
	return m_renderer_manager.get();
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
IDeviceManager* P2PMSClientImpl::GetDeviceManager()
{
	return &DeviceManager::Instance();
}

////////////////////////////////////////////////////////////////////////////////////////////////////

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
bool P2PMSClientImpl::CreateWebRtcThreads()
{
	m_network_thread = rtc::Thread::CreateWithSocketServer();
	if (!m_network_thread || !m_network_thread->Start()) {
		LOG_ERR("Start network thread failed");
		return false;
	}

	m_signaling_thread = rtc::Thread::Create();
	if (!m_signaling_thread || !m_signaling_thread->Start()) {
		LOG_ERR("Start signaling thread failed");
		return false;
	}

	m_worker_thread = rtc::Thread::Create();
	if (!m_worker_thread || !m_worker_thread->Start()) {
		LOG_ERR("Start worker thread failed");
		return false;
	}

	return true;
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
bool P2PMSClientImpl::InitMsgDispatcher(const std::string& server_addr)
{
	m_msg_dispatcher = std::make_shared<WsMsgDispatcher>();
	if (!m_msg_dispatcher->Init(kWaitResTimeoutMs,
		std::bind(&P2PMSClientImpl::OnDispatcherEvent, this, _1))) {
		LOG_ERR("Failed to initialize WebSocket dispatcher");
		return false;
	}

	m_msg_dispatcher->Register(LOGIN_RES,
		std::bind(&P2PMSClientImpl::OnLoginRes, this, _1, _2));
	m_msg_dispatcher->Register(LOGIN_NOTIFY,
		std::bind(&P2PMSClientImpl::OnLoginNotify, this, _1, _2));
	m_msg_dispatcher->Register(LOGOUT_NOTIFY,
		std::bind(&P2PMSClientImpl::OnLogoutNotify, this, _1, _2));
	m_msg_dispatcher->Register(CALL_MAKE_NOTIFY,
		std::bind(&P2PMSClientImpl::OnCallNotify, this, _1, _2));

	if (!m_msg_dispatcher->Start(server_addr)) {
		LOG_ERR("Failed to start WebSocket dispatcher with URI: {}", server_addr);
		return false;
	}

	return true;
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void P2PMSClientImpl::ScheduleTimer()
{
	if (!m_timer_running || !m_sdk_thread) return;
	m_sdk_thread->PostDelayedTask(
		[this]() {
			if (m_timer_running) {
				OnTimer();
				ScheduleTimer();
			}
		},
	webrtc::TimeDelta::Millis(1000));
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void P2PMSClientImpl::OnTimer()
{
	if (m_call_agent) {
		m_call_agent->OnTimer();
	}
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
bool P2PMSClientImpl::CreatePeerConnectionFactory()
{
	m_pc_factory = webrtc::CreatePeerConnectionFactory(
		m_network_thread.get(),   // network thread
		m_worker_thread.get(),    // worker thread
		m_signaling_thread.get(), // signaling thread
		DeviceManager::Instance().GetADM(),
		webrtc::CreateBuiltinAudioEncoderFactory(),
		webrtc::CreateBuiltinAudioDecoderFactory(),
		std::make_unique<webrtc::VideoEncoderFactoryTemplate<
		webrtc::LibvpxVp8EncoderTemplateAdapter,
		webrtc::LibvpxVp9EncoderTemplateAdapter,
		webrtc::OpenH264EncoderTemplateAdapter,
		webrtc::LibaomAv1EncoderTemplateAdapter>>(),
		std::make_unique<webrtc::VideoDecoderFactoryTemplate<
		webrtc::LibvpxVp8DecoderTemplateAdapter,
		webrtc::LibvpxVp9DecoderTemplateAdapter,
		webrtc::OpenH264DecoderTemplateAdapter,
		webrtc::Dav1dDecoderTemplateAdapter>>(),
		nullptr,  // audio_mixer
		nullptr); // audio_processing
	if (!m_pc_factory) {
		LOG_ERR("Failed to create PeerConnectionFactory");
		return false;
	}

	return true;
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void P2PMSClientImpl::OnLoginRes(const Message& msg, CBType cbt)
{
	assert(m_client_handler);
	assert(m_sdk_thread);

	m_sdk_thread->PostTask([this, msg, cbt]() {
		if (cbt == CBType::Timeout) {
			LOG_ERR("Received non-normal message type: {}", cbt);
			m_client_handler->OnLoginResult(false);
		}
		else {
			try {
				ResMsgData res = msg.data.get<ResMsgData>();
				if (res.code == 0) {
					LoginResData resData = res.data.get<LoginResData>();
					if (!resData.users.empty()) {
						m_online_user_list = resData.users;
						LOG_INF("Logged in successfully, users: {}", m_online_user_list.size());
					}
					m_logined = true;
					m_client_handler->OnLoginResult(true);
				}
				else {
					LOG_ERR("Login failed: {}", res.msg);
					m_client_handler->OnLoginResult(false);
				}
			}
			catch (const std::exception& e) {
				LOG_ERR("Failed to parse login response: {}", e.what());
				m_client_handler->OnLoginResult(false);
			}
		}
	});	
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void P2PMSClientImpl::OnLoginNotify(const Message& msg, CBType cbt)
{
	assert(m_client_handler);
	assert(m_sdk_thread);

	m_sdk_thread->PostTask([this, msg, cbt]() {
		LoginNotifyData notify_data;
		try {
			notify_data = msg.data.get<LoginNotifyData>();
			LOG_INF("User logged in: {}", notify_data.user_id);
		} catch (const std::exception& e) {
			LOG_ERR("Failed to parse login notification data: {}", e.what());
		}

		if (notify_data.user_id.empty()) {
			LOG_ERR("Received empty user ID in login notification");
			return;
		}

		for (const auto& user_id : m_online_user_list) {
			if (user_id == notify_data.user_id) {
				LOG_INF("User {} is already in the online user list", notify_data.user_id);
				return;
			}
		}

		// 添加用户到在线用户列表
		m_online_user_list.push_back(notify_data.user_id);

		LOG_INF("User {} added to online user list, total users: {}", 
			notify_data.user_id, m_online_user_list.size());

		// 通知客户端处理登录通知
		m_client_handler->OnLoginNotify(notify_data.user_id);

		// 发送登录通知的确认消息
		m_msg_dispatcher->SendMsg(MakeLoginAckMsg(msg.rid, notify_data.user_id));
	});
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void P2PMSClientImpl::OnLogoutNotify(const Message& msg, CBType cbt)
{
	assert(m_client_handler);
	assert(m_sdk_thread);

	m_sdk_thread->PostTask([this, msg, cbt]() {
		try {
			LogoutNotifyData notify_data = msg.data.get<LogoutNotifyData>();
			auto it = std::find(m_online_user_list.begin(), m_online_user_list.end(), notify_data.user_id);
			if (it != m_online_user_list.end()) {
				m_online_user_list.erase(it);
				LOG_INF("User {} logged out, remaining online users: {}", 
					notify_data.user_id, m_online_user_list.size());
				m_client_handler->OnLogoutNotify(notify_data.user_id);
			} else {
				LOG_INF("User {} not found in online user list", notify_data.user_id);
			}

			if (m_call_agent && 
				IsP2PCall(m_call_agent->GetCallMode()) &&
				m_call_agent->GetCallState() == CallState::Connected &&
				m_call_agent->GetPeerUserId() == notify_data.user_id) {
				LOG_INF("Current call agent user {} logged out, terminating call", notify_data.user_id);
				m_call_agent->TerminateCall();
				m_call_agent.reset();
			}
		} catch (const std::exception& e) {
			LOG_ERR("Failed to parse logout notification data: {}", e.what());
		}
	});
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void P2PMSClientImpl::SendCallMakeAck(const std::string& rid, int code, const std::string& msg,
	const CallMakeNotifyData& notify_data)
{
	auto ack = MakeCallMakeAckMsg(rid, code, msg, 
		notify_data.call_type, 
		notify_data.call_id,
		notify_data.caller_id, 
		notify_data.callee_id);

	if (!m_msg_dispatcher->SendMsg(ack)) {
		LOG_ERR("Failed to send call make ack message");
	}
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void P2PMSClientImpl::OnCallNotify(const Message& msg, CBType cbt)
{
	assert(m_sdk_thread);
	m_sdk_thread->PostTask([this, msg]() {
		CallMakeNotifyData notify_data;
		if (!ParseRequest(msg, notify_data)) {
			LOG_ERR("Parse call make notify failed");
			return;
		}

		LOG_INF("Received call notification: type={}, caller_id={}, call_id={}",
			notify_data.call_type, notify_data.caller_id, notify_data.call_id);

		auto start = std::chrono::steady_clock::now();
		bool accepted = m_client_handler->OnIncomingCall(
			notify_data.call_type, 
			notify_data.caller_id, 
			notify_data.call_id);
		auto end = std::chrono::steady_clock::now();

		auto elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();

		if (!accepted) {
			LOG_WRN("Rejected incoming call");
			SendCallMakeAck(msg.rid, 1, "reject", notify_data);
			return;
		}

		if (elapsed_ms > 10000) {
			LOG_WRN("OnIncomingCall handler timeout ({} ms), ignore incoming call", elapsed_ms);
			SendCallMakeAck(msg.rid, 1, "timeout", notify_data);
			return;
		}

		SendCallMakeAck(msg.rid, 0, "accept", notify_data);

		if (notify_data.call_type == "p2p") {
			m_call_agent = std::make_shared<P2PCallAgent>(
				CallRole::Callee, 
				m_user_id, 
				m_pc_factory,
				this, 
				m_sdk_thread, 
				m_msg_dispatcher);
		} else if (notify_data.call_type == "p2sp") {
			m_call_agent = std::make_shared<P2SPCallAgent>(
				CallRole::Callee, 
				m_user_id, 
				m_pc_factory,
				this, 
				m_sdk_thread, 
				m_msg_dispatcher);
		} else {
			LOG_ERR("Unknown call type: {}", notify_data.call_type);
			return;
		}

		if (ERR_OK != m_call_agent->Init()) {
			LOG_ERR("Failed to initialize call agent for incoming call");
			m_call_agent.reset();
			return;
		}

		if (ERR_OK != m_call_agent->IncomingCall(notify_data)) {
			LOG_ERR("Incoming P2P call handled failed");
		}
	});
}

//--------------------------------------------------------------------------------------------------
// 注意：不能切换到 sdk 线程，否则可能会形成死锁
//--------------------------------------------------------------------------------------------------
void P2PMSClientImpl::OnDispatcherEvent(DispatcherEventType event_type)
{
	LOG_INF("P2PMSClient::OnDispatcherEvent:{}", event_type);

	std::shared_ptr<std::promise<bool>> promise;
	{
		std::lock_guard<std::mutex> lock(m_init_mutex);
		promise = m_init_promise;
		if (m_init_promise) {
			m_init_promise.reset(); // 立即reset，防止后续再set
		}
	}

	if (event_type == DispatcherEventType::CONNECTED) {
		if (promise) {
			promise->set_value(true);
		}
		LOG_INF("WebSocket connected.");
	} else if (event_type == DispatcherEventType::DISCONNECTED) {
		if (promise) {
			promise->set_value(false);
		}
		LOG_INF("WebSocket disconnected.");
	} else {
		LOG_ERR("Unknown WebSocket event type: {}", static_cast<int>(event_type));
	}
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void P2PMSClientImpl::OnCallStateChanged(std::string call_id, CallState state)
{
	LOG_INF("Call state changed to: {}", ToString(state));

	if (state == CallState::Terminated) {
		if (m_call_agent) {
			m_call_agent.reset();
		}
	}

	m_client_handler->OnCallStateChanged(call_id, state);
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void P2PMSClientImpl::OnStartPublish(const std::string& user_id, const MediaInfo& media)
{
	LOG_INF("OnStartPublishMedia: user_id={}, media={}", user_id, ToString(media));

	m_client_handler->OnStartPublish(user_id, media);
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void P2PMSClientImpl::OnStopPublish(const std::string& user_id, const MediaInfo& media)
{
	LOG_INF("OnStopPublishMedia: user_id={}, media={}", user_id, ToString(media));

	m_client_handler->OnStopPublish(user_id, media);
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void P2PMSClientImpl::OnStartPublishResult(const MediaInfo& media, bool result)
{
	LOG_INF("OnStartPublishMediaResult: user_id={}, media={}", media.media_id, ToString(media));

	m_client_handler->OnStartPublishResult(media, result);
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void P2PMSClientImpl::OnStopPublishResult(const MediaInfo& media, bool result)
{
	LOG_INF("OnStopPublishResult: user_id={}, media={}", media.media_id, ToString(media));

	m_client_handler->OnStopPublishResult(media, result);
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void P2PMSClientImpl::OnStartSubscribeResult(const std::string& user_id, const MediaInfo& media,
	bool result)
{
	LOG_INF("OnStartSubscribeResult: user_id={}, media={}, result={}", user_id, ToString(media), 
		result);

	m_client_handler->OnStartSubscribeResult(user_id, media, result);
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void P2PMSClientImpl::OnStopSubscribeResult(const std::string& user_id, const MediaInfo& media,
	bool result)
{
	LOG_INF("OnStopSubscribeResult: user_id={}, media={}, result={}", user_id, ToString(media), 
		result);

	m_client_handler->OnStopSubscribeResult(user_id, media, result);
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void P2PMSClientImpl::OnAddTrack(const std::string& user_id, const MediaInfo& media,
	rtc::scoped_refptr<webrtc::MediaStreamTrackInterface> track)
{
	LOG_INF("OnAddTrack: user_id={}, media={}, track={}, kind={}", user_id, 
		ToString(media), track->id(), track->kind());

	if (track->kind() == webrtc::MediaStreamTrackInterface::kVideoKind) {
		assert(m_renderer_manager);
		rtc::scoped_refptr<webrtc::VideoTrackInterface> video_track(
			static_cast<webrtc::VideoTrackInterface*>(track.get()));
		m_renderer_manager->AddVideoTrack(media, video_track);
	} else if (track->kind() == webrtc::MediaStreamTrackInterface::kAudioKind) {
		LOG_INF("Audio track added: {}", track->id());
	} else {
		LOG_ERR("Unsupported track kind: {}", track->kind());
	} 
}
 
//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void P2PMSClientImpl::OnRemoveTrack(const std::string& user_id, const MediaInfo& media)
{
	LOG_INF("OnRemoveTrack: user_id={}, media={}, track={}", user_id, ToString(media));

	if (media.media_type == "video") {
		assert(m_renderer_manager);
		m_renderer_manager->RemoveVideoTrack(media);
	} else if (media.media_type == "video") {
		LOG_INF("Audio track removed");
	} else {
		LOG_ERR("Unsupported track kind");
	}
}

} // namespace p2pms
